Repository navigation
merge: adding a gate is the supervisor's call; changing one stays the owner's - #217
Merged
Merged
Conversation
… owner's The owner ruled on 2026-11-13 that dev-only tools like linters don't need their approval, but merge still refused any change to settings.gates without an answered gate-change question, even a new shellcheck gate. So the supervisor had to ask against the ruling or leave the check off. merge now lands a PR that only adds gates without a question: new entries after main's, every gate main has byte-identical and in order, none removed or renamed, no key repeated under gates, each new one a lowercase name with only cmd, paths, junit or mode. The new gate runs at the PR head first (a failing one is refused unless in ratchet mode, since removing it would need the owner), and the merge logs it as its own gate-add act, a new constitutional class. Everything else that touches gates, and any worktree_setup or worktree_files change, stays gate-change. The check reads the trees, so no log line changes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e owner after it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t gate counts Fable's review of #217: - A plan PR is a planner's, so only the supervisor's own chore adds a gate without the owner; a gate's command runs with the supervisor's environment, so that is the line, and the docs say nothing checks "dev-only" for it. - A pure reorder of main's gates compared equal canonically and merged with no owner and no act; gates now compare as written, so it is gate-change. - No gates key on main counts as none, so the first gate is an addition. - The gate-add act is not reversible by the supervisor (removal is the owner's); a failing ratchet pre-run is marked preexisting. - supervisor.md's owner-settings line names the exception. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
Reviewed adversarially at 4bd0f13, looking for any way the supervisor could widen its own authority:
CI passes 9 of 9, after a rerun of a flaky job. Merging. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
merge --chorelands a supervisor's chore PR that only adds gates tosettings.gateswithout an owner question. A plan PR is a planner's, so a gate added there is still the owner's, and ticket PRs can't change settings at all. It's the supervisor's call, and merge logs it as its owngate-addact, a new constitutional class. Changing, loosening, reordering, renaming or removing a gate stays the owner'sgate-change.A PR counts as "only adds" when all of these hold. They're read from the base and head trees, never the log:
ownerSettingsChangednow comparesgatesas written, not canonically, so a pure reorder of main's gates is agate-changetoo (before this PR it slipped through as "unchanged").gateskey on main counts as none, so the first gate is an addition.gatesin the head's settings block, becauseJSON.parsesilently keeps the last one, so a second"lint"could replace main's without anyone seeing it.cmd,paths,junit,mode). Itscmdis one line of plain text, itsjunitis a path inside the checkout, and itsmodeisenforcedorratchet. A field the kit adds later stays the owner's to set.Merge then runs each new gate at the PR head in a seeded detached worktree, with the same
gateEnvevery gate gets. If one fails, merge refuses unless it's in ratchet mode, because a broken gate would block every later merge and only the owner could remove it. Once the PR lands, merge logsgate-add(added gate shellcheck ("shellcheck …") to settings.gates by merging …) with refs to the target and the merge act.When
gatesstill needs the owner, the refusal says why:changes owner settings: gates (gate-change) (gates: it changes lint; only adding a gate is the supervisor's).Docs:
constitution.md(the decision table, the §2 note on constitutional classes, the §4 one-way-door bullet),supervisor.md("Development-only tools are yours") andquality.md(the owner-settings paragraph) say that adding a gate that runs only dev-only tools is the supervisor's call, recorded as its own act, and that changing or removing a gate stays the owner'sgate-change.worktree_setup and worktree_files stay owner-only
Neither one gets an additive exception. Both act on the checkout before every gate runs. An appended
worktree_setupstep runs ahead of main's gates in the same worktree and can change what they see: rewrite files, shadow a tool on PATH, poisonDEVTEAM_CACHE. An addedworktree_filesentry copies a file from the main checkout over the gate's checkout, or brings a production key into every worktree (the existingproduction-key-in-worktreesfinding). So even a pure addition to either can change what an existing gate sees. A linter gate rarely needs them, since the repo's own lockfile and the existing setup install the tool.Abuse cases
DEVTEAM_HOME) and the shared cache, just as every existing gate's command and test code can. So it gives the supervisor nothing it can't already do in its own shell. That's why the exemption is limited to the supervisor's own chore PR and logged as an act the owner reviews and can correct (not reversible by the supervisor). The docs say this plainly rather than claiming the code enforces "dev-only". A new gate gets nothing existing gates don't: the samegateEnv(the supervisor's environment plusDEVTEAM_MAIN/HOME/CACHE), the same detached worktree, the samesh -c. Its shape is limited to the known fields, so it can't carry anenv, atimeoutor a future field. Itsjunitcan't point outside the checkout, and its command is one plain line.worktree_files(the way a production key reaches a checkout) stays owner-only.gatesis refused, and existing entries must be byte-identical. Gates already run in a fixed order and none can skip another (each failure is collected, none stops the loop), and a new gate runs after all of main's.Not covered (follow-ups)
links.<id>.gateskeeps the owner's per-entrylink set --approvedflow. Extending the additive rule to it needs the entry-hash acts to learn about it.gate-change, as asked. The run at the head is there so a broken one doesn't land.Why
The owner, 2026-11-13: "yes, I rule that dev-only tools like linters don't need my approval. I've said this over and over again." v0.44.2 (#198) put that ruling in
constitution.md§4 andsupervisor.md, butmergestill refused anysettings.gateschange without agate-changeanswer, a new shellcheck gate included. That left the supervisor two choices: ask the owner against the ruling, or leave the check off. Adding a check only makes merges stricter, so it's safe. Removing, loosening or editing a gate is how a protection gets switched off, so that stays the owner's.Tested
test/gate-add.test.mjs(new, Node 24.14.1, 6/6):gate-addwith refs to the merge act, and lands on main after main's gates.gates, and a repeated top-levelgates.env), ajunitof../.., a multi-line cmd, and a failing new gate. A failing ratchet gate passes.act --class gate-addplus hand-writtengate-addandgate-changeact lines don't let an edit through, and an addition needs no line at all.worktree_setupandworktree_fileschanges beside an added gate are still refused.gates.test.cmd). The first gate on a main with nogateskey lands.test/kit.test.mjs(Node 24.14.1): 82/82.merge-admin,componentsandrepo-linkstests: green.chore ?guard).Fable adversarial review, and what changed
supervisor.md's owner-settings line names the exception. "byte-identical" became "identical as JSON, field order included". The constitution no longer claims the code checks "dev-only".preexisting. Thegate-addact isreversible: false. The guard is nowadded.length.--approvedpassed beside an add-only PR is consumed (existing behaviour).Migration
None. Every gate change that needed the owner before still does, except a pure addition. Existing projects pick it up with the release.
🤖 Generated with Claude Code